#include "flashled.h"
#include "userconfig.h"

int flashled_Clock(uintptr_t wObjectAddr);
int flashled_Run(uintptr_t wObjectAddr);

// Define a global flashled base of type gmsi_base_t
static gmsi_base_t s_tFlashledBase;
uint8_t chTestbuffer[5] = {0x11, 0x22, 0x33, 0x44, 0x55};
static uint8_t s_chFlashMasterData[50];
static uint8_t s_chFlashSlaveData[PROTOCOL_MAX] = {0x5a, 0xa5, 0xff, 0x1F, 0x00, 0xff, 0xff};;
// Define and initialize a global flashled base configuration of type gmsi_base_cfg_t
gmsi_base_cfg_t s_tFlashledBaseCfg = {
    .wId = FLASHLED,                 // Set the ID to FLASHLED
    .wParent = 0,                   // Set the parent to 0
    .FcnInterface = {
        .Clock = flashled_Clock,     // Set the Clock function to flashled_Clock
        .Run = flashled_Run,         // Set the Run function to flashled_Run
    },
};

gcoroutine_handle_t tGcoroutineFlashledHandle = {
    .bIsRunning = false,
    .pfcn = NULL,
};

/**
 * Function: flashled_gcoroutine
 * ----------------------------
 * This function is a coroutine that handles different states of the flashled object. It receives 
 * a parameter, converts it to an flashled object, and uses a switch statement to handle different 
 * states. This function returns fsm_rt_on_going if it is still running, or fsm_rt_cpl if it has 
 * completed its task. Once the coroutine has completed its task, it will be removed from the 
 * coroutine list.
 *
 * Parameters: 
 * pvParam: The parameter to be converted to an flashled object.
 *
 * Returns: 
 * fsm_rt_on_going if the function is still running, or fsm_rt_cpl if the function has completed 
 * its task.
 */
fsm_rt_t flashled_gcoroutine(void *pvParam)
{
    static uint8_t s_eState = 0;
    fsm_rt_t tFsm = fsm_rt_on_going;
    flashled_t *ptThis = (flashled_t *)pvParam;

    // Check if ptThis is not NULL
    if (ptThis == NULL) {
        GLOG_PRINTF("Error: ptThis is NULL.\n");
        return fsm_rt_err;
    }

    switch(s_eState)
    {
        case 0:
            GLOG_PRINTF("get flashled event");
            s_eState++;
            break;
        case 1:
            GLOG_PRINTF("finish get flashled event handle");
            fsm_cpl();
            break;
        default:
            fsm_cpl();
        break;
    }
    fsm_on_going(); 
}

/**
 * Function: flashled_EventHandle
 * ----------------------------
 * This function handles events for the flashled object. It checks if the object is not NULL, 
 * retrieves the events for the object, and handles these events. If the event Event_PacketReceived 
 * is set, it gets the message from the object and handles it accordingly.
 *
 * Parameters: 
 * ptThis: The address of the flashled object.
 * wEvent: The event to handle.
 *
 * Returns: 
 * None
 */
static void flashled_EventHandle(flashled_t *ptThis, uint32_t wEvent)
{
    int wLength = 0;
    GMSI_MSG_DECLARE(FlashMasterData, 20);
    // Check if ptThis is not NULL
    if (ptThis == NULL) {
        GLOG_PRINTF("ptThis is NULL.");
        return;
    }

    // Check if the event Event_SyncMissed is set
    if(wEvent & Gmsi_Event_Transition)
    {
        // Handle the event Event_PacketReceived
        //wLength = gbase_MessageGet(ptThis->ptBase, s_chFlashMasterData);
        //if(wLength > 0)
        if(gbase_MessagePend(ptThis->ptBase, GMSI_MSG_GET_HANDLE(FlashMasterData)) > 0)
        {
            uint8_t* pchMasterDatat = GMSI_MSG_GET_BUFFER(FlashMasterData);
            if(pchMasterDatat[FRAME_ADDR] < 7)
            {
                s_chFlashSlaveData[2] = pchMasterDatat[FRAME_ADDR];
            }
            else
            {
                s_chFlashSlaveData[2] = 0xff;
            }

            switch (pchMasterDatat[FRAME_FUNCTION])
            {
            case FUNCTION_V_LIGHT:
                s_chFlashSlaveData[3] = 1;
                break;
            case FUNCTION_N_LIGHT:
                s_chFlashSlaveData[3] = 2;
                break;
            case FUNCTION_H_LIGHT:
                s_chFlashSlaveData[3] = 4;
                break;
            case FUNCTION_UVLIGHT:
                s_chFlashSlaveData[3] = 8;
                break;
            case FUNCTION_D_LIGHT:
                s_chFlashSlaveData[3] = 2;
                break;  
            case FUNCTION_UVLIGHT405:
                s_chFlashSlaveData[3] = 0x10;
                break;
            default:
                break;
            }
            s_chFlashSlaveData[4] = pchMasterDatat[FRAME_VALUE];
            s_chFlashSlaveData[FRAME_CRC1] = 0xff;
            s_chFlashSlaveData[FRAME_CRC2] = 0xff;
            ptThis->Write(ptThis->wFd, s_chFlashSlaveData, 7);
            // send event to correspondent
            if(pchMasterDatat[FRAME_VALUE])
                pchMasterDatat[FRAME_VALUE] = 0x11;
            else
                pchMasterDatat[FRAME_VALUE] = 0x01;
            pchMasterDatat[FRAME_FUNCTION] += 1;
            // pchMasterDatat 
            //gbase_MessagePost(CORRESPONDENT, s_chFlashMasterData, 7);
        }
    }

}

/**
 * Function: flashled_Run
 * ----------------------------
 * This function is mounted in the GMSI run function list and is called within a while(1) loop. 
 * It receives events and messages from other objects. When a corresponding event is triggered, 
 * it executes the appropriate coroutine. The function gets the flashled object from the given 
 * address, retrieves the events for the object, and handles these events. This function returns 
 * GMSI_SUCCESS if it runs successfully.
 *
 * Parameters: 
 * wObjectAddr: The address of the flashled object.
 *
 * Returns: 
 * GMSI_SUCCESS if the function runs successfully.
 */
int flashled_Run(uintptr_t wObjectAddr)
{
    int wRet = GMSI_SUCCESS;
    uint32_t wEvent;
    uint16_t hwLength;
    // Get the flashled object from the given address
    flashled_t *ptThis = (flashled_t *)wObjectAddr;

    // Check if ptThis is not NULL
    if (ptThis == NULL) {
        GLOG_PRINTF("ptThis is NULL.");
        return GMSI_EFAIL;
    }

    // handle uart
    hwLength = ptThis->Read(ptThis->wFd, s_chFlashSlaveData);
    if(hwLength)
    {

    }
    
    // Get the events for the flashled object
    wEvent = gbase_EventPend(ptThis->ptBase);
    // If there are any events, handle them
    if(wEvent)
        flashled_EventHandle(ptThis, wEvent);

    // Logic or state machine programs

    return wRet;
}

/**
 * Function: flashled_Clock
 * ----------------------------
 * This function is called in a 1ms interrupt. It gets the flashled object from the given address 
 * and performs operations on it. This function returns GMSI_SUCCESS if it runs successfully.
 *
 * Parameters: 
 * wObjectAddr: The address of the flashled object.
 *
 * Returns: 
 * GMSI_SUCCESS if the function runs successfully.
 */
int flashled_Clock(uintptr_t wObjectAddr)
{
     static uint16_t s_hwTestcount = 0; 
    // Get the flashled object from the given address
    flashled_t *ptThis = (flashled_t *)wObjectAddr;

    int wRet = GMSI_SUCCESS;
    
    // Perform operations on ptThis

    return wRet;
}

/**
 * Function: flashled_Init
 * ----------------------------
 * This function initializes an flashled object. It converts the given addresses to pointers, 
 * checks if the pointers are not NULL, copies the configuration members to the object, initializes 
 * the hardware, and registers the object in the GMSI list.
 *
 * Parameters: 
 * wObjectAddr: The address of the flashled object.
 * wObjectCfgAddr: The address of the flashled configuration object.
 *
 * Returns: 
 * GMSI_SUCCESS if the function runs successfully, GMSI_EAGAIN if the object cannot be registered 
 * in the GMSI list, or GMSI_FAILURE if any of the pointers is NULL.
 */
int flashled_Init(uintptr_t wObjectAddr, uintptr_t wObjectCfgAddr)
{
    // Convert the given addresses to pointers
    flashled_t *ptThis = (flashled_t *)wObjectAddr;
    flashled_cfg_t *ptCfg = (flashled_cfg_t *)wObjectCfgAddr;

    // Check if the pointers are not NULL
    if (ptThis == NULL || ptCfg == NULL) {
        GLOG_PRINTF("Error: ptThis or ptCfg is NULL.");
        return GMSI_EFAIL;
    }

    /* Copy the configuration members to the object */
    ptThis->wFd = ptCfg->wFd;
    ptThis->Read = ptCfg->Read;
    ptThis->Write = ptCfg->Write;
    /* Initialize the hardware */

    // Register the object in the GMSI list
    ptThis->ptBase = &s_tFlashledBase;
    if (ptThis->ptBase == NULL) {
        return GMSI_EAGAIN;
    } else {
        s_tFlashledBaseCfg.wParent = wObjectAddr;
        return gbase_Init(ptThis->ptBase, &s_tFlashledBaseCfg);
    }
}